package com.gitee.wsl.mathematics.interpolator

import com.gitee.wsl.ext.number.convertRange
import com.gitee.wsl.ext.number.convertRangeClamped
import com.gitee.wsl.mathematics.interpolator.ext.toRatio
import kotlin.jvm.JvmInline

@JvmInline
value class Ratio(override val value: Double) : PercentOrRatio {
    constructor(ratio: Float) : this(ratio.toDouble())
    constructor(value: Int, maximum: Int) : this(value.toFloat() / maximum.toFloat())
    constructor(value: Float, maximum: Float) : this(value / maximum)
    constructor(value: Double, maximum: Double) : this(value / maximum)

    override fun create(value: Double) = Ratio(value)

    override fun toString(): String = "$value"

    override fun coerceToDefault() = create(value.coerceIn(.0, 1.0))

    operator fun plus(other: Ratio)  = create(value + other.value)
    operator fun minus(other: Ratio) = create(value - other.value)
    operator fun times(other: Ratio)  = create(value * other.value)
    override operator fun div(d: Number)             = create(value / d.toDouble())

    /**
     * Operator to allow to write `20.pct * 3` (= 0.6)
     */
    override operator fun unaryMinus()         = create(-value)
    override operator fun unaryPlus()          = create(+value)

    companion object {
        val ZERO = Ratio(0.0)
        val QUARTER = Ratio(.25)
        val HALF = Ratio(.5)
        val ONE = Ratio(1.0)
        val DOUBLE = Ratio(2.0)
        val NaN = Ratio(Float.NaN)

         fun fromValueInRange(value: Number, min: Number, max: Number): Ratio =
            value.toDouble().convertRange(min.toDouble(), max.toDouble(), 0.0, 1.0).toRatio()

         fun fromValueInRangeClamped(value: Number, min: Number, max: Number): Ratio =
            value.toDouble().convertRangeClamped(min.toDouble(), max.toDouble(), 0.0, 1.0).toRatio()

        inline fun forEachRatio(steps: Int, include0: Boolean = true, include1: Boolean = true, block: (ratio: Ratio) -> Unit) {
            val NS = steps - 1
            val NSd = NS.toDouble()
            val start = if (include0) 0 else 1
            val end = if (include1) NS else NS - 1
            for (n in start..end) {
                val ratio = n.toFloat() / NSd
                block(ratio.toRatio())
            }
        }
    }
}


